package cn.yll.api.conf;

import cn.yll.common.exception.BsErrEnum;
import cn.yll.common.exception.BsException;
import cn.yll.common.module.RestModel;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.UndeclaredThrowableException;

/**
 * 顶层统一异常处理类，包含功能异常转换为restModel，发送异常告警等
 * @author yangll
 */
@Slf4j
@RestControllerAdvice
public class ControllerExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = BsException.class)
    public RestModel<?> handleBsException(HttpServletResponse response, BsException e) {
        log.error("业务异常捕获：{}==={}", e.getMessage(), JSON.toJSONString(e.getBsErrEnum()));
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        return new RestModel(e.getBsErrEnum().getCode(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = UndeclaredThrowableException.class)
    public RestModel<?> handleFlowException(HttpServletResponse response, UndeclaredThrowableException e) {
        Throwable cause = e.getCause();
        if (cause instanceof FlowException) {
            log.error("限流fallback异常捕获：{}", ((FlowException) cause).getRule());
            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
        } else if (cause instanceof DegradeException) {
            log.error("熔断降级fallback异常捕获：{}", ((DegradeException) cause).getRule());
            response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
        } else {
            log.error("UndeclaredThrowableException异常捕获：", e);
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        }
        return new RestModel(BsErrEnum.REQUEST_TIME_ERROR);
    }

    /**
     * spring参数不合法
     */
    @ResponseBody
    @ExceptionHandler(value = {
            HttpMessageNotReadableException.class,
            MethodArgumentTypeMismatchException.class,
            MissingServletRequestParameterException.class,
            MethodArgumentNotValidException.class,
            NumberFormatException.class,
            BindException.class})
    public RestModel<?> processMethodArgumentTypeMismatchError(HttpServletRequest req, HttpServletResponse response, RuntimeException exception) {
        log.error("参数异常捕获url:" + req.getRequestURI(), exception);
        response.setStatus(HttpStatus.RESET_CONTENT.value());
        return new RestModel(BsErrEnum.PARAM_ILLEGAL);
    }

    /**
     * 接口方法不支持
     */
    @ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public RestModel<?> processMethodNotSupportedException(HttpServletRequest req, HttpServletResponse response, HttpRequestMethodNotSupportedException exception) {
        log.error("请求方式错误捕获url:" + req.getRequestURI(), exception);
        response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.value());
        return new RestModel(BsErrEnum.REQUEST_ERROR);
    }

    /**
     * 上传图片尺寸过大
     */
    @ResponseBody
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public RestModel<?> maxUploadSizeExceededException(HttpServletRequest req, HttpServletResponse response, MaxUploadSizeExceededException exception) {
        log.error("上传文件异常捕获url:" + req.getRequestURI(), exception);
        response.setStatus(HttpStatus.PAYLOAD_TOO_LARGE.value());
        return new RestModel(406, "您上传的图片尺寸过大");
    }

    @ResponseBody
    @ExceptionHandler(value = Throwable.class)
    public RestModel<?> handleGlobalException(HttpServletRequest req, HttpServletResponse response, Throwable exception) {
        log.error("系统非自定义异常捕获url:" + req.getRequestURI(), exception);
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        return new RestModel(BsErrEnum.SYSTEM_ERROR);
    }
}
